﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Zen4SyncPOCO.POCO.Admin;
using System.Xml.Linq;
using System.Xml.XPath;
using Zen4SyncPOCO.POCO.Scenario;
using Zen4SyncPOCO.POCO.Enum.Scenario;
using Zen4SyncPOCO.POCO.SQLServerExecution;
using Zen4SyncPOCO.Enum.Admin;
using Zen4SyncPOCO.POCO.SQLCEExecution;
using Zen4SyncPOCO.POCO.SQLCEBackup;
using Zen4SyncPOCO.POCO.MEF;
using Zen4SyncPOCO.POCO.MergeSync;
using Zen4SyncPOCO.Enum.MergeSync;

namespace Zen4SyncPOCO.ClientTestSessionDescriptorHandler
{
    internal class ClientTestSessionDescriptorReader
    {
        internal ClientSession UnserializeClientTestSessionDescriptor(XElement clientTestSessionDescriptor)
        {
            XElement clientSessionElement = clientTestSessionDescriptor.Element("clientSession");

            ClientSession clientSession = new ClientSession
            {
                clientSession_id = new Guid(clientSessionElement.Attribute("id").Value),
                clientSession_testServerId = new Guid(clientSessionElement.Attribute("testServerId").Value),
                clientSession_testSessionId = new Guid(clientSessionElement.Attribute("testSessionId").Value),
                clientSession_enableLog = Boolean.Parse(clientSessionElement.Element("enableLog").Attribute("value").Value),
                clientSession_scheduledStartDateTime = DateTime.Parse(clientSessionElement.Element("scheduledStartDateTime").Value),
                clientSession_activityContexts = UnserializeActivityContexts(clientSessionElement.Element("testScenario").Elements("activity")),
                clientSession_testScenario = UnserializeTestScenario(clientSessionElement.Element("testScenario"))
            };

            return clientSession;
        }

        private TestScenario UnserializeTestScenario(XElement testScenario)
        {
            TestScenario testScenarioPOCO = new TestScenario
            {
                testScenario_id = new Guid(testScenario.Attribute("id").Value),
                testScenario_activities = UnserializeActivities(testScenario.Elements("activity"))
            };

            return testScenarioPOCO;
        }

        /// <summary>
        /// Unserializes the &lt;activity&gt; elements.
        /// This method sorts the activities on their executionOrder, so it's not needed to give a sorted
        /// list of activities.
        /// </summary>
        /// <param name="activities"></param>
        /// <returns></returns>
        private List<Activity> UnserializeActivities(IEnumerable<XElement> activities)
        {
            List<Activity> activitiesList = new List<Activity>();

            foreach (var activity in activities.OrderBy(ac => Convert.ToInt32(ac.Element("executionOrder").Value)))
            {
                Activity activityPOCO = new Activity
                {
                    activity_id = new Guid(activity.Attribute("id").Value),
                    activity_executionOrder = Convert.ToInt32(activity.Element("executionOrder").Value),
                    activity_preWaitingTimeInSeconds = Convert.ToInt32(activity.Element("preWaitingTimeInSeconds").Value),
                    activity_postWaitingTimeInSeconds = Convert.ToInt32(activity.Element("postWaitingTimeInSeconds").Value)
                };

                activityPOCO.activity_activityTypeEnum = (ActivityTypeEnum)System.Enum.Parse(typeof(ActivityTypeEnum), activity.Element("activityItem").Attribute("type").Value);

                switch (activityPOCO.activity_activityTypeEnum)
                {
                    case ActivityTypeEnum.SyncSession:
                        activityPOCO.activity_syncSession = UnserializeSyncSession(activity.Element("activityItem").Element("syncSession"), activity.Element("activityContext").Element("overridingData"));
                        break;
                    case ActivityTypeEnum.SqlServerExecution:
                        activityPOCO.activity_sqlServerExecution = UnserializeSqlServerExecution(activity.Element("activityItem").Element("sqlServerExecution"));
                        break;
                    case ActivityTypeEnum.SqlServerCheckData:
                        activityPOCO.activity_sqlServerCheckData = UnserializeSqlServerCheckData(activity.Element("activityItem").Element("sqlServerCheckData"));
                        break;
                    case ActivityTypeEnum.SqlCeExecution:
                        activityPOCO.activity_sqlCeExecution = UnserializeSqlCeExecution(activity.Element("activityItem").Element("sqlCeExecution"));
                        break;
                    case ActivityTypeEnum.SqlCeCheckData:
                        activityPOCO.activity_sqlCeCheckData = UnserializeSqlCeCheckData(activity.Element("activityItem").Element("sqlCeCheckData"));
                        break;
                    case ActivityTypeEnum.SqlCeBackup:
                        activityPOCO.activity_sqlCeBackup = UnserializeSqlCeBackup(activity.Element("activityItem").Element("sqlCeBackup"));
                        break;
                    case ActivityTypeEnum.MefExecution:
                        activityPOCO.activity_mefExecution = UnserializeMefExecution(activity.Element("activityItem").Element("mefExecution"));
                        break;
                    default:
                        break;
                }

                activitiesList.Add(activityPOCO);
            }

            return activitiesList;
        }


        private List<ActivityContext> UnserializeActivityContexts(IEnumerable<XElement> activities)
        {
            List<ActivityContext> activityContextsList = new List<ActivityContext>();

            foreach (XElement activity in activities)
            {

                /*
                 * Set the overridingData to null since this class will use the overriding data values (when present) to set
                 * the property values of the corresponding activity.
                 * So at the end of the reading, the actitivies property values have taken the overriding data into account
                 * and we dont need overridingData anymore.
                 */ 
                ActivityContext newContext = new ActivityContext
                {
                    activityContext_id = new Guid(activity.Element("activityContext").Attribute("id").Value),
                    activityContext_activityId = new Guid(activity.Attribute("id").Value),
                    activityContext_overridingData = null
                };

                // The context type depends on the activity type
                ActivityTypeEnum activityType = (ActivityTypeEnum)System.Enum.Parse(typeof(ActivityTypeEnum), activity.Element("activityItem").Attribute("type").Value);
                switch (activityType)
                {
                    case ActivityTypeEnum.SyncSession:
                        newContext.activityContext_syncSessionContext = UnserializeSyncSessionContext(activity.Element("activityContext").Element("syncSessionContext"));
                        break;
                    case ActivityTypeEnum.SqlServerExecution:
                        newContext.activityContext_sqlServerExecutionContext = UnserializeSqlServerExecutionContext(activity.Element("activityContext").Element("sqlServerExecutionContext"));
                        break;
                    case ActivityTypeEnum.SqlServerCheckData:
                        newContext.activityContext_sqlServerCheckDataContext = UnserializeSqlServerCheckDataContext(activity.Element("activityContext").Element("sqlServerCheckDataContext"));
                        break;
                    case ActivityTypeEnum.SqlCeExecution:
                        newContext.activityContext_sqlCeExecutionContext = UnserializeSqlceExecutionContext(activity.Element("activityContext").Element("sqlCeExecutionContext"));
                        break;
                    case ActivityTypeEnum.SqlCeCheckData:
                        newContext.activityContext_sqlCeCheckDataContext = UnserializeSqlCeCheckDataContext(activity.Element("activityContext").Element("sqlCeCheckDataContext"));
                        break;
                    case ActivityTypeEnum.SqlCeBackup:
                        newContext.activityContext_sqlCeBackupContext = UnserializeSqlCeBackupContext(activity.Element("activityContext").Element("sqlCeBackupContext"));
                        break;
                    case ActivityTypeEnum.MefExecution:
                        newContext.activityContext_mefExecutionContext = UnserializeMefExecutionContext(activity.Element("activityContext").Element("mefExecutionContext"));
                        break;
                    default:
                        break;
                }

                activityContextsList.Add(newContext);
            }

            return activityContextsList;
        }


        #region Activities

        private SyncSession UnserializeSyncSession(XElement syncSession, XElement overridingData)
        {
            XElement syncSessionOverridingData = null;
            if (overridingData != null)
            {
                syncSessionOverridingData = overridingData.Element("syncSession");
            }

            Publisher publisherPOCO = new Publisher
            {
                publisher_id = new Guid(syncSession.Element("publisher").Attribute("id").Value),
                publisher_networkTypeEnum = (NetworkTypeEnum)System.Enum.Parse(typeof(NetworkTypeEnum), XmlTools.GetBestValue(syncSessionOverridingData, "publisher/@networkType", syncSession)),
                publisher_securityTypeEnum = (SecurityTypeEnum)System.Enum.Parse(typeof(SecurityTypeEnum), XmlTools.GetBestValue(syncSessionOverridingData, "publisher/@securityType", syncSession)),
                publisher_name = XmlTools.GetBestValue(syncSessionOverridingData, "publisher/name", syncSession),
                publisher_address = XmlTools.GetBestValue(syncSessionOverridingData, "publisher/address", syncSession),
                publisher_database = XmlTools.GetBestValue(syncSessionOverridingData, "publisher/database", syncSession),
                publisher_login = XmlTools.GetBestValue(syncSessionOverridingData, "publisher/login", syncSession),
                publisher_password = XmlTools.GetBestValue(syncSessionOverridingData, "publisher/password", syncSession)
            };

            Distributor distributorPOCO = new Distributor
            {
                distributor_id = new Guid(syncSession.Element("distributor").Attribute("id").Value),
                distributor_networkTypeEnum = (NetworkTypeEnum)System.Enum.Parse(typeof(NetworkTypeEnum), XmlTools.GetBestValue(syncSessionOverridingData, "distributor/@networkType", syncSession)),
                distributor_securityTypeEnum = (SecurityTypeEnum)System.Enum.Parse(typeof(SecurityTypeEnum), XmlTools.GetBestValue(syncSessionOverridingData, "distributor/@securityType", syncSession)),
                distributor_name = XmlTools.GetBestValue(syncSessionOverridingData, "distributor/name", syncSession),
                distributor_address = XmlTools.GetBestValue(syncSessionOverridingData, "distributor/address", syncSession),
                distributor_login = XmlTools.GetBestValue(syncSessionOverridingData, "distributor/login", syncSession),
                distributor_password = XmlTools.GetBestValue(syncSessionOverridingData, "distributor/password", syncSession),
                distributor_profileName = XmlTools.GetBestValue(syncSessionOverridingData, "distributor/profileName", syncSession)
            };

            Internet internetPOCO = new Internet
            {
                internet_id = new Guid(syncSession.Element("internet").Attribute("id").Value),
                internet_name = XmlTools.GetBestValue(syncSessionOverridingData, "internet/name", syncSession),
                internet_url = XmlTools.GetBestValue(syncSessionOverridingData, "internet/url", syncSession),
                internet_login = XmlTools.GetBestValue(syncSessionOverridingData, "internet/login", syncSession),
                internet_password = XmlTools.GetBestValue(syncSessionOverridingData, "internet/password", syncSession),
                internet_proxyServer = XmlTools.GetBestValue(syncSessionOverridingData, "internet/proxyServer", syncSession),
                internet_proxyLogin = XmlTools.GetBestValue(syncSessionOverridingData, "internet/proxyLogin", syncSession),
                internet_proxyPassword = XmlTools.GetBestValue(syncSessionOverridingData, "internet/proxyPassword", syncSession)
            };

            SyncSession syncSessionPOCO = new SyncSession
            {
                syncSession_id = new Guid(syncSession.Attribute("id").Value),
                syncSession_exchangeTypeEnum = (ExchangeTypeEnum)System.Enum.Parse(typeof(ExchangeTypeEnum), XmlTools.GetBestValue(syncSessionOverridingData, "./@exchangeType", syncSession)),
                syncSession_snapshotTransferTypeEnum = (SnapshotTransfertTypeEnum)System.Enum.Parse(typeof(SnapshotTransfertTypeEnum), XmlTools.GetBestValue(syncSessionOverridingData, "./@snapshotTransferType", syncSession)),
                syncSession_compressionLevel = Convert.ToInt16(XmlTools.GetBestValue(syncSessionOverridingData, "compressionLevel", syncSession)),
                syncSession_connectionRetryTimeout = Convert.ToInt16(XmlTools.GetBestValue(syncSessionOverridingData, "connectionRetryTimeout", syncSession)),
                syncSession_connectTimeout = Convert.ToInt16(XmlTools.GetBestValue(syncSessionOverridingData, "connectionTimeout", syncSession)),
                syncSession_hostName = XmlTools.GetBestValue(syncSessionOverridingData, "hostName", syncSession),
                syncSession_loginTimeout = Convert.ToInt16(XmlTools.GetBestValue(syncSessionOverridingData, "loginTimetout", syncSession)),
                syncSession_postSyncCleanup = Convert.ToInt16(XmlTools.GetBestValue(syncSessionOverridingData, "postSyncCleanup", syncSession)),
                syncSession_publication = XmlTools.GetBestValue(syncSessionOverridingData, "publication", syncSession),
                syncSession_queryTimeout = Convert.ToInt16(XmlTools.GetBestValue(syncSessionOverridingData, "queryTimeout", syncSession)),
                syncSession_receiveTimeout = Convert.ToInt16(XmlTools.GetBestValue(syncSessionOverridingData, "receiveTimeout", syncSession)),
                syncSession_sendTimeout = Convert.ToInt16(XmlTools.GetBestValue(syncSessionOverridingData, "sendTimeout", syncSession)),
                syncSession_distributor = distributorPOCO,
                syncSession_internet = internetPOCO,
                syncSession_publisher = publisherPOCO
            };

            return syncSessionPOCO;
        }

        private MefExecution UnserializeMefExecution(XElement mefExecution)
        {
            MefExecution POCO = new MefExecution
            {
                mefExecution_id = new Guid(mefExecution.Attribute("id").Value),
                mefExecution_mefComponentDirectory = mefExecution.Element("mefComponentDirectory").Value,
                mefExecution_metadata = mefExecution.Element("metadata").FirstNode.ToString()
            };
            return POCO;
        }

        private SqlceCheckData UnserializeSqlCeCheckData(XElement sqlCeCheckData)
        {
            SqlceCheckData POCO = new SqlceCheckData
            {
                sqlceCheckData_id = new Guid(sqlCeCheckData.Attribute("id").Value),
                sqlceCheckData_sqlQuery = sqlCeCheckData.Element("sqlQuery").Value,
                sqlceCheckData_expectedValue = Encoding.UTF8.GetBytes(sqlCeCheckData.Element("expectedValue").Value),
                sqlceCheckData_sqlValueTypeEnum = (SqlValueTypeEnum)System.Enum.Parse(typeof(SqlValueTypeEnum), sqlCeCheckData.Element("expectedValue").Attribute("type").Value)
            };
            return POCO;
        }

        private SqlServerCheckData UnserializeSqlServerCheckData(XElement sqlServerCheckData)
        {
            SqlServerCheckData POCO = new SqlServerCheckData
            {
                sqlServerCheckData_id = new Guid(sqlServerCheckData.Attribute("id").Value),
                sqlServerCheckData_sqlQuery = sqlServerCheckData.Element("sqlQuery").Value,
                sqlServerCheckData_expectedValue = Encoding.UTF8.GetBytes(sqlServerCheckData.Element("expectedValue").Value),
                sqlServerCheckData_sqlValueTypeEnum = (SqlValueTypeEnum)System.Enum.Parse(typeof(SqlValueTypeEnum), sqlServerCheckData.Element("expectedValue").Attribute("type").Value)
                
            };
            return POCO;
        }

        private SqlceBackup UnserializeSqlCeBackup(XElement sqlCeBackup)
        {
            SqlceBackup POCO = new SqlceBackup
            {
                sqlceBackup_id = new Guid(sqlCeBackup.Attribute("id").Value)
            };
            return POCO;
        }

        private SqlceExecution UnserializeSqlCeExecution(XElement sqlceExecution)
        {
            SqlceExecution POCO = new SqlceExecution
            {
                sqlceExecution_id = new Guid(sqlceExecution.Attribute("id").Value),
                sqlceExecution_sqlQuery = sqlceExecution.Element("sqlQuery").Value
            };
            return POCO;
        }

        private SqlServerExecution UnserializeSqlServerExecution(XElement sqlServerExecution)
        {
            SqlServerExecution POCO = new SqlServerExecution
            {
                sqlServerExecution_id = new Guid(sqlServerExecution.Attribute("id").Value),
                sqlServerExecution_sqlQuery = sqlServerExecution.Element("sqlQuery").Value
            };
            return POCO;
        }

        #endregion


        #region Activity Contexts

        private SyncSessionContext UnserializeSyncSessionContext(XElement syncSessionContext)
        {
            XElement subscriberElement = syncSessionContext.Element("subscriber");
            Subscriber subscriberPOCO = new Subscriber
            {
                subscriber_id = new Guid(subscriberElement.Attribute("id").Value),
                subscriber_validateTypeEnum = (ValidateTypeEnum)System.Enum.Parse(typeof(ValidateTypeEnum), subscriberElement.Attribute("validateType").Value),
                subscriber_name = subscriberElement.Element("name").Value,
                subscriber_hostName = subscriberElement.Element("hostName").Value,
                subscriber_connectionString = subscriberElement.Element("connectionString").Value,
                subscriber_deleteSdfIfItExists = Boolean.Parse(subscriberElement.Element("deleteSdfIfItExists").Attribute("value").Value),
                subscriber_sdfFileName = subscriberElement.Element("sdfFileName").Value,
                subscriber_sdfFileDirectory = subscriberElement.Element("sdfFileDirectory").Value,
                subscriber_reinitializeSubscription = Boolean.Parse(subscriberElement.Element("reinitializeSubscription").Attribute("value").Value),
                subscriber_uploadBeforeReinitialize = Boolean.Parse(subscriberElement.Element("uploadBeforeReinitialize").Attribute("value").Value)
            };

            SyncSessionContext contextPOCO = new SyncSessionContext
            {
                syncSessionContext_id = new Guid(syncSessionContext.Attribute("id").Value),
                syncSessionContext_subscriber = subscriberPOCO 
            };

            return contextPOCO;
        }

        private MefExecutionContext UnserializeMefExecutionContext(XElement mefExecutionContext)
        {
            MefExecutionContext contextPOCO = new MefExecutionContext
            {
                mefExecutionContext_id = new Guid(mefExecutionContext.Attribute("id").Value),
                mefExecutionContext_metadata = mefExecutionContext.Element("metadata").FirstNode.ToString()
            };
            
            return contextPOCO;
        }
        
        private SqlceCheckDataContext UnserializeSqlCeCheckDataContext(XElement sqlCeCheckDataContext)
        {
            // SQL Query parameters
            List<SqlceCheckDataParameter> parametersPOCO = new List<SqlceCheckDataParameter>();

            if (sqlCeCheckDataContext.Element("sqlQueryParameters") != null)
            { 
                foreach (var param in sqlCeCheckDataContext.Element("sqlQueryParameters").Elements("parameter"))
                {
                    SqlceCheckDataParameter paramPOCO = new SqlceCheckDataParameter
                    {
                        sqlceCheckDataParameter_name = param.Attribute("name").Value,
                        sqlceCheckDataParameter_value = Encoding.UTF8.GetBytes(param.Value),
                        sqlceCheckDataParameter_sqlValueTypeEnum = (SqlValueTypeEnum)System.Enum.Parse(typeof(SqlValueTypeEnum), param.Attribute("type").Value)
                    };
                    parametersPOCO.Add(paramPOCO);
                }
            }

            // Context
            SqlceCheckDataContext contextPOCO = new SqlceCheckDataContext
            {
                sqlceCheckDataContext_id = new Guid(sqlCeCheckDataContext.Attribute("id").Value),
                sqlceCheckDataContext_connectionString = sqlCeCheckDataContext.Element("connectionString").Value,
                sqlceCheckDataContext_parameters = parametersPOCO
            };

            return contextPOCO;
        }

        private SqlServerCheckDataContext UnserializeSqlServerCheckDataContext(XElement sqlServerCheckDataContext)
        {
            // SQL query parameters
            List<SqlServerCheckDataParameter> parametersPOCO = new List<SqlServerCheckDataParameter>();

            if (sqlServerCheckDataContext.Element("sqlQueryParameters") != null)
            { 
                foreach (var param in sqlServerCheckDataContext.Element("sqlQueryParameters").Elements("parameter"))
                {
                    SqlServerCheckDataParameter paramPOCO = new SqlServerCheckDataParameter
                    {
                        sqlServerCheckDataParameter_name = param.Attribute("name").Value,
                        sqlServerCheckDataParameter_value = Encoding.UTF8.GetBytes(param.Value),
                        sqlServerCheckDataParameter_sqlValueTypeEnum = (SqlValueTypeEnum)System.Enum.Parse(typeof(SqlValueTypeEnum), param.Attribute("type").Value)
                    };
                    parametersPOCO.Add(paramPOCO);
                }
            }

            // Context
            SqlServerCheckDataContext contextPOCO = new SqlServerCheckDataContext
            {
                sqlServerCheckDataContext_id = new Guid(sqlServerCheckDataContext.Attribute("id").Value),
                sqlServerCheckDataContext_connectionString = sqlServerCheckDataContext.Element("connectionString").Value,
                sqlServerCheckDataContext_parameters = parametersPOCO
            };

            return contextPOCO;
        }

        private SqlceExecutionContext UnserializeSqlceExecutionContext(XElement sqlceExecutionContext)
        {
            // SQL query parameters
            List<SqlceExecutionParameter> parametersPOCO = new List<SqlceExecutionParameter>();

            if (sqlceExecutionContext.Element("sqlQueryParameters") != null)
            { 
                foreach (var param in sqlceExecutionContext.Element("sqlQueryParameters").Elements("parameter"))
                {
                    SqlceExecutionParameter paramPOCO = new SqlceExecutionParameter
                    {
                        sqlceExecutionParameter_name = param.Attribute("name").Value,
                        sqlceExecutionParameter_value = Encoding.UTF8.GetBytes(param.Value),
                        sqlceExecutionParameter_sqlValueTypeEnum = (SqlValueTypeEnum)System.Enum.Parse(typeof(SqlValueTypeEnum), param.Attribute("type").Value)
                    };
                    parametersPOCO.Add(paramPOCO);
                }
            }

            // Context
            SqlceExecutionContext contextPOCO = new SqlceExecutionContext
            {
                sqlceExecutionContext_id = new Guid(sqlceExecutionContext.Attribute("id").Value),
                sqlCeExecutionContext_connectionString = sqlceExecutionContext.Element("connectionString").Value,
                sqlceExecutionContext_parameters = parametersPOCO
            };

            return contextPOCO;
        }

        private SqlServerExecutionContext UnserializeSqlServerExecutionContext(XElement sqlServerExecutionContext)
        {
            // SQL query parameters
            List<SqlServerExecutionParameter> parametersPOCO = new List<SqlServerExecutionParameter>();

            if (sqlServerExecutionContext.Element("sqlQueryParameters") != null)
            { 
                foreach (var param in sqlServerExecutionContext.Element("sqlQueryParameters").Elements("parameter"))
                {
                    SqlServerExecutionParameter paramPOCO = new SqlServerExecutionParameter
                    {
                        sqlServerExecutionParameter_name = param.Attribute("name").Value,
                        sqlServerExecutionParameter_value = Encoding.UTF8.GetBytes(param.Value),
                        sqlServerExecutionParameter_sqlValueTypeEnum = (SqlValueTypeEnum)System.Enum.Parse(typeof(SqlValueTypeEnum), param.Attribute("type").Value)
                    };
                    parametersPOCO.Add(paramPOCO);
                }
            }

            // Context
            SqlServerExecutionContext contextPOCO = new SqlServerExecutionContext
            {
                sqlServerExecutionContext_id = new Guid(sqlServerExecutionContext.Attribute("id").Value),
                sqlServerExecutionContext_connectionString = sqlServerExecutionContext.Element("connectionString").Value,
                sqlServerExecutionContext_parameters = parametersPOCO
            };

            return contextPOCO;
        }

        private SqlceBackupContext UnserializeSqlCeBackupContext(XElement sqlceBackupContext)
        {
            SqlceBackupContext POCO = new SqlceBackupContext
            {
                sqlceBackupContext_id = new Guid(sqlceBackupContext.Attribute("id").Value),
                sqlceBackupContext_sdfFileName = sqlceBackupContext.Element("sdfFileName").Value,
                sqlceBackupContext_sdfFileDirectory = sqlceBackupContext.Element("sdfFileDirectory").Value
            };
            return POCO;
        }

        #endregion
    }
}
